﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Kinect;
using Kinect.Gesture;
using Kinect.Toolbox.Record;
using System.IO;

namespace Kinect.Toolbox
{
    public enum E_Animation
    {
        RotationLeft = 0,
        RotationRight,
        RotationUp,
        RotationDown,
        ZoomIn,
        ZoomOut,
        Waiting,
        Count
    };


    public class GestureManager
    {
        KinectSensor kinectSensor;

        protected List<GestureDetector> _GestureDetectorList;
        protected List<GestureDetector> GestureDetectorList
        {
            get
            {
                return _GestureDetectorList;
            }
        }

        TemplatedGestureDetector circleGestureRecognizer;
        readonly ColorStreamManager colorManager = new ColorStreamManager();
        readonly DepthStreamManager depthManager = new DepthStreamManager();
        AudioStreamManager audioManager;

        readonly ContextTracker contextTracker = new ContextTracker();
        readonly AlgorithmicPostureDetector algorithmicPostureRecognizer = new AlgorithmicPostureDetector();
        TemplatedPostureDetector templatePostureDetector;
        private bool recordNextFrameForPosture;
        bool displayDepth;

        string circleKBPath;
        string letterT_KBPath;

        KinectRecorder recorder;
        KinectReplay replay;

        BindableNUICamera nuiCamera;

        private Skeleton[] skeletons;

        public EventHandler<GestureEventArgs> GestureDetected;

        public EventHandler<ClickEventArgs> RightHandClicked;
        public EventHandler<ClickEventArgs> LeftHandClicked;

        protected int ScreenWidth;
        protected int ScreenHeight;

        protected GestureManager()
        {

        }

        public GestureManager(int width, int height)
        {
            ScreenHeight = height;
            ScreenWidth = width;
            circleKBPath = Path.Combine(Environment.CurrentDirectory, @"data\circleKB.save");
            letterT_KBPath = Path.Combine(Environment.CurrentDirectory, @"data\t_KB.save");
            _GestureDetectorList = new List<GestureDetector>();
            try
            {
                //listen to any status change for Kinects
                KinectSensor.KinectSensors.StatusChanged += Kinects_StatusChanged;

                //loop through all the Kinects attached to this PC, and start the first that is connected without an error.
                foreach (KinectSensor kinect in KinectSensor.KinectSensors)
                {
                    if (kinect.Status == KinectStatus.Connected)
                    {
                        kinectSensor = kinect;
                        break;
                    }
                }

                if (KinectSensor.KinectSensors.Count == 0)
                    System.Diagnostics.Debug.WriteLine("No Kinect found");
                else
                    Initialize();

            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(ex.Message);
            }
        }

        void Kinects_StatusChanged(object sender, StatusChangedEventArgs e)
        {
            switch (e.Status)
            {
                case KinectStatus.Connected:
                    if (kinectSensor == null)
                    {
                        kinectSensor = e.Sensor;
                        Initialize();
                    }
                    break;
                case KinectStatus.Disconnected:
                    if (kinectSensor == e.Sensor)
                    {
                        Clean();
                        System.Diagnostics.Debug.WriteLine("Kinect was disconnected");
                    }
                    break;
                case KinectStatus.NotReady:
                    break;
                case KinectStatus.NotPowered:
                    if (kinectSensor == e.Sensor)
                    {
                        Clean();
                        System.Diagnostics.Debug.WriteLine("Kinect is no more powered");
                    }
                    break;
                default:
                    System.Diagnostics.Debug.WriteLine("Unhandled Status: " + e.Status);
                    break;
            }
        }


        private void Clean()
        {
            foreach (GestureDetector gd in _GestureDetectorList)
            {
                gd.OnGestureDetected -= OnGestureDetected;
            }


            if (audioManager != null)
            {
                audioManager.Dispose();
                audioManager = null;
            }

            CloseGestureDetector();

            ClosePostureDetector();


            if (recorder != null)
            {
                recorder.Stop();
                recorder = null;
            }

            if (kinectSensor != null)
            {
                kinectSensor.DepthFrameReady -= kinectSensor_DepthFrameReady;
                kinectSensor.SkeletonFrameReady -= kinectRuntime_SkeletonFrameReady;
                kinectSensor.ColorFrameReady -= kinectRuntime_ColorFrameReady;
                kinectSensor.Stop();
                kinectSensor = null;
            }
        }
        void kinectSensor_DepthFrameReady(object sender, DepthImageFrameReadyEventArgs e)
        {
            if (replay != null && !replay.IsFinished)
                return;

            using (var frame = e.OpenDepthImageFrame())
            {
                if (frame == null)
                    return;

                if (recorder != null && ((recorder.Options & KinectRecordOptions.Depth) != 0))
                {
                    recorder.Record(frame);
                }

                if (!displayDepth)
                    return;

                depthManager.Update(frame);
            }
        }

        void kinectRuntime_ColorFrameReady(object sender, ColorImageFrameReadyEventArgs e)
        {
            if (replay != null && !replay.IsFinished)
                return;

            using (var frame = e.OpenColorImageFrame())
            {
                if (frame == null)
                    return;

                if (recorder != null && ((recorder.Options & KinectRecordOptions.Color) != 0))
                {
                    recorder.Record(frame);
                }

                if (displayDepth)
                    return;

                colorManager.Update(frame);
            }
        }

        void kinectRuntime_SkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e)
        {
            if (replay != null && !replay.IsFinished)
                return;

            using (SkeletonFrame frame = e.OpenSkeletonFrame())
            {
                if (frame == null)
                    return;

                if (recorder != null && ((recorder.Options & KinectRecordOptions.Skeletons) != 0))
                    recorder.Record(frame);

                frame.GetSkeletons(ref skeletons);
                int skeletonNum = 0;
                foreach (Skeleton skeleton in skeletons)
                {
                    if (skeleton.TrackingState == SkeletonTrackingState.Tracked)
                        skeletonNum++;
                }
                //if (skeletonNum>0)
                //System.Diagnostics.Debug.WriteLine("Tracked Skeleton count="+skeletonNum);
                if (skeletons.All(s => s.TrackingState == SkeletonTrackingState.NotTracked))
                    return;

                ProcessFrame(frame);
            }
        }

        void templatePostureDetector_PostureDetected(string posture)
        {
            System.Diagnostics.Debug.WriteLine("Give me a......." + posture);
        }
        /// <summary>
        /// Change the Method to find the 
        /// </summary>
        /// <param name="frame"></param>
        void ProcessFrame(ReplaySkeletonFrame frame)
        {
            Dictionary<int, string> stabilities = new Dictionary<int, string>();
            List<Skeleton> skeletonList = new List<Skeleton>();
            Skeleton primarySkeleton = null;
            if (IsInGestureProcessing)
                return;
            foreach (var skeleton in frame.Skeletons)
            {
                if (skeleton.TrackingState != SkeletonTrackingState.Tracked)
                    continue;
                if (primarySkeleton != null)
                {
                    if (primarySkeleton.Position.Z > skeleton.Position.Z)
                        primarySkeleton = skeleton;
                }
                else
                    primarySkeleton = skeleton;

            }
            if (primarySkeleton == null)
                return;

            contextTracker.Add(primarySkeleton.Position.ToVector3(), primarySkeleton.TrackingId);
            stabilities.Add(primarySkeleton.TrackingId, contextTracker.IsStableRelativeToCurrentSpeed(primarySkeleton.TrackingId) ? "Stable" : "Non stable");
            if (!contextTracker.IsStableRelativeToCurrentSpeed(primarySkeleton.TrackingId))
                return;
            
            foreach (GestureDetector gd in _GestureDetectorList)
            {
                if (gd is SwipeGestureDetector || gd is PushPullGestureDetector || gd is ZoomGestureDetector)
                    gd.Add(primarySkeleton, kinectSensor);
            }

        }

        protected bool IsInGestureProcessing = false;
        public void GestureAnimationFinished()
        {
            IsInGestureProcessing = false;
        }
        void OnGestureDetected(object sender, GestureEventArgs e)
        {
            if (this.GestureDetected != null)
            {
                IsInGestureProcessing = true;
                GestureDetected(sender, e);

            }
            //System.Diagnostics.Debug.WriteLine(string.Format("{0} : {1}", gesture, DateTime.Now));
        }


        void ClosePostureDetector()
        {
            if (templatePostureDetector == null)
                return;

            using (Stream recordStream = File.Create(letterT_KBPath))
            {
                templatePostureDetector.SaveState(recordStream);
            }
            templatePostureDetector.PostureDetected -= templatePostureDetector_PostureDetected;
        }

        void CloseGestureDetector()
        {
            if (circleGestureRecognizer == null)
                return;

            using (Stream recordStream = File.Create(circleKBPath))
            {
                circleGestureRecognizer.SaveState(recordStream);
            }
            circleGestureRecognizer.OnGestureDetected -= OnGestureDetected;
        }

        protected virtual void Initialize()
        {
            if (kinectSensor == null)
                return;
            audioManager = new AudioStreamManager(kinectSensor.AudioSource);

            kinectSensor.ColorStream.Enable(ColorImageFormat.RgbResolution640x480Fps30);
            kinectSensor.ColorFrameReady += kinectRuntime_ColorFrameReady;

            kinectSensor.DepthStream.Enable(DepthImageFormat.Resolution640x480Fps30);
            kinectSensor.DepthFrameReady += kinectSensor_DepthFrameReady;

            kinectSensor.SkeletonStream.Enable(new TransformSmoothParameters
            {
                Smoothing = 0.5f,
                Correction = 0.5f,
                Prediction = 0.5f,
                JitterRadius = 0.05f,
                MaxDeviationRadius = 0.04f
            });
            kinectSensor.SkeletonFrameReady += kinectRuntime_SkeletonFrameReady;


            _GestureDetectorList = new List<GestureDetector>();
            //Activator.CreateInstance();
            _GestureDetectorList.Add(new SwipeGestureDetector());
            _GestureDetectorList.Add(new PushPullGestureDetector());
            _GestureDetectorList.Add(new ZoomGestureDetector());

            foreach (GestureDetector gestureDetector in _GestureDetectorList)
            {
                gestureDetector.OnGestureDetected += OnGestureDetected;
            }

            kinectSensor.Start();

            LoadCircleGestureDetector();
            LoadLetterTPostureDetector();

            nuiCamera = new BindableNUICamera(kinectSensor);


        }
        void LoadCircleGestureDetector()
        {
            using (Stream recordStream = File.Open(circleKBPath, FileMode.OpenOrCreate))
            {
                circleGestureRecognizer = new TemplatedGestureDetector("Circle", recordStream);


                circleGestureRecognizer.OnGestureDetected += new EventHandler<GestureEventArgs>(OnGestureDetected);

            }
        }

        void LoadLetterTPostureDetector()
        {
            using (Stream recordStream = File.Open(letterT_KBPath, FileMode.OpenOrCreate))
            {
                templatePostureDetector = new TemplatedPostureDetector("T", recordStream);
                templatePostureDetector.PostureDetected += templatePostureDetector_PostureDetected;
            }
        }


    }

}
